package main

import (
	"database/sql"
	"encoding/json"
	"reflect"

	"test.com/fusion"
	"test.com/worlddb"
)

type spellDialogTableRow struct {
	SpellID   uint32 `json:"spellID"`
	SpellName string `json:"spellName"`
	SpellDesc string `json:"spellDesc"`
}

func getAllSpell4DialogTable(db *sql.DB) ([]*spellDialogTableRow, error) {
	spellNames, err := getAllKeyI18Ns(db,
		"spell_info", "spellID", worlddb.STRING_TEXT_TYPE_SPELL_NAME)
	if err != nil {
		return nil, err
	}
	spellDescs, err := getAllKeyI18Ns(db,
		"spell_info", "spellID", worlddb.STRING_TEXT_TYPE_SPELL_DESC)
	if err != nil {
		return nil, err
	}
	var spellRows []*spellDialogTableRow
	for spellID, spellName := range spellNames {
		spellDesc := spellDescs[spellID]
		spellRows = append(spellRows,
			&spellDialogTableRow{spellID, spellName, spellDesc})
	}
	return spellRows, nil
}

func getAllSpellNames(db *sql.DB) (map[uint32]string, error) {
	return getAllKeyI18Ns(db,
		"spell_info", "spellID", worlddb.STRING_TEXT_TYPE_SPELL_NAME)
}

func getSpellInfo(db *sql.DB, spellID uint32) (*worlddb.SpellInfo, error) {
	var spellInfo *worlddb.SpellInfo
	spellInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellInfo), "`spellID`=?", spellID)
	if err != nil {
		return nil, err
	}
	if spellInfoVals.Len() < 1 {
		return nil, sql.ErrNoRows
	}
	return spellInfoVals.Index(0).Interface().(*worlddb.SpellInfo), nil
}

func getSpellLevelIndexes(db *sql.DB, spellID uint32) ([]*spellLevelIndex, error) {
	var spellLevelIndexes []*spellLevelIndex
	rows, err := db.Query(
		"SELECT `spellLevelID` FROM `spell_level_info` WHERE `spellID`=? "+
			"ORDER BY `spellLevelID`", spellID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var sli spellLevelIndex
		if err := rows.Scan(&sli.SpellLevelID); err == nil {
			spellLevelIndexes = append(spellLevelIndexes, &sli)
		} else {
			return nil, err
		}
	}
	return spellLevelIndexes, nil
}

func getSpellLevelInfo(db *sql.DB, spellLevelID uint32) (*worlddb.SpellLevelInfo, error) {
	var spellLevelInfo *worlddb.SpellLevelInfo
	spellLevelInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelInfo), "`spellLevelID`=?", spellLevelID)
	if err != nil {
		return nil, err
	}
	if spellLevelInfoVals.Len() < 1 {
		return nil, sql.ErrNoRows
	}
	return spellLevelInfoVals.Index(0).Interface().(*worlddb.SpellLevelInfo), nil
}

func getSpellLevelEffectIndexes(db *sql.DB, spellLevelID uint32) ([]*spellLevelEffectIndex, error) {
	var spellLevelEffectIndexes []*spellLevelEffectIndex
	rows, err := db.Query("SELECT `spellLevelEffectID`,`spellEffectType`,`spellSelectType`,"+
		"`spellStageTrigger`,`spellEffectStyle` FROM `spell_level_effect_info` "+
		"WHERE `spellLevelID`=? ORDER BY `spellLevelEffectID`", spellLevelID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var slei spellLevelEffectIndex
		var err = rows.Scan(&slei.SpellLevelEffectID, &slei.SpellEffectType,
			&slei.SpellSelectType, &slei.SpellStageTrigger, &slei.SpellEffectStyle)
		if err == nil {
			spellLevelEffectIndexes = append(spellLevelEffectIndexes, &slei)
		} else {
			return nil, err
		}
	}
	return spellLevelEffectIndexes, nil
}

func getSpellLevelEffectInfo(db *sql.DB, spellLevelEffectID uint32) (*worlddb.SpellLevelEffectInfo, error) {
	var spellLevelEffectInfo *worlddb.SpellLevelEffectInfo
	spellLevelEffectInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelEffectInfo), "`spellLevelEffectID`=?", spellLevelEffectID)
	if err != nil {
		return nil, err
	}
	if spellLevelEffectInfoVals.Len() < 1 {
		return nil, sql.ErrNoRows
	}
	return spellLevelEffectInfoVals.Index(0).Interface().(*worlddb.SpellLevelEffectInfo), nil
}

func getSpellInstanceJsonData(db *sql.DB, spellID uint32) ([]byte, error) {
	var err error
	var spellProto spellWebPrototype
	spellProto.SpellName, err =
		getI18N(db, worlddb.STRING_TEXT_TYPE_SPELL_NAME, spellID)
	if err != nil && err != sql.ErrNoRows {
		return nil, err
	}
	spellProto.SpellDesc, err =
		getI18N(db, worlddb.STRING_TEXT_TYPE_SPELL_DESC, spellID)
	if err != nil && err != sql.ErrNoRows {
		return nil, err
	}
	spellProto.SpellInfo, err = getSpellInfo(db, spellID)
	if err != nil {
		return nil, err
	}
	spellProto.SpellLevelIndexes, err =
		getSpellLevelIndexes(db, spellProto.SpellInfo.SpellID)
	if err != nil {
		return nil, err
	}
	if len(spellProto.SpellLevelIndexes) > 0 {
		spellProto.SpellLevelInfo, err = getSpellLevelInfo(
			db, spellProto.SpellLevelIndexes[0].SpellLevelID)
		if err != nil {
			return nil, err
		}
		spellProto.SpellLevelEffectIndexes, err = getSpellLevelEffectIndexes(
			db, spellProto.SpellLevelInfo.SpellLevelID)
		if err != nil {
			return nil, err
		}
	} else {
		spellProto.SpellLevelInfo = &worlddb.SpellLevelInfo{}
	}
	if len(spellProto.SpellLevelEffectIndexes) > 0 {
		spellProto.SpellLevelEffectDesc, err = getI18N(
			db, worlddb.STRING_TEXT_TYPE_BUFF_DESC,
			spellProto.SpellLevelEffectIndexes[0].SpellLevelEffectID)
		if err != nil && err != sql.ErrNoRows {
			return nil, err
		}
		spellProto.SpellLevelEffectInfo, err = getSpellLevelEffectInfo(
			db, spellProto.SpellLevelEffectIndexes[0].SpellLevelEffectID)
		if err != nil {
			return nil, err
		}
	} else {
		spellProto.SpellLevelEffectInfo = &worlddb.SpellLevelEffectInfo{}
	}
	spellData, err := json.Marshal(
		fusion.MarshalEntityToInterface(&spellProto, "json"))
	if err != nil {
		return nil, err
	}
	return spellData, nil
}

func getSpellLevelInstanceJsonData(
	db *sql.DB, spellID, spellLevelID uint32) ([]byte, error) {
	var err error
	var spellProto spellLevelWebPrototype
	spellProto.SpellLevelIndexes, err = getSpellLevelIndexes(db, spellID)
	if err != nil {
		return nil, err
	}
	spellProto.SpellLevelInfo, err = getSpellLevelInfo(db, spellLevelID)
	if err != nil {
		return nil, err
	}
	spellProto.SpellLevelEffectIndexes, err = getSpellLevelEffectIndexes(
		db, spellProto.SpellLevelInfo.SpellLevelID)
	if err != nil {
		return nil, err
	}
	if len(spellProto.SpellLevelEffectIndexes) > 0 {
		spellProto.SpellLevelEffectDesc, err = getI18N(
			db, worlddb.STRING_TEXT_TYPE_BUFF_DESC,
			spellProto.SpellLevelEffectIndexes[0].SpellLevelEffectID)
		if err != nil && err != sql.ErrNoRows {
			return nil, err
		}
		spellProto.SpellLevelEffectInfo, err = getSpellLevelEffectInfo(db,
			spellProto.SpellLevelEffectIndexes[0].SpellLevelEffectID)
		if err != nil {
			return nil, err
		}
	} else {
		spellProto.SpellLevelEffectInfo = &worlddb.SpellLevelEffectInfo{}
	}
	spellData, err := json.Marshal(
		fusion.MarshalEntityToInterface(&spellProto, "json"))
	if err != nil {
		return nil, err
	}
	return spellData, nil
}

func getSpellLevelEffectInstanceJsonData(
	db *sql.DB, spellLevelID, spellLevelEffectID uint32) ([]byte, error) {
	var err error
	var spellProto spellLevelEffectWebPrototype
	spellProto.SpellLevelEffectIndexes, err =
		getSpellLevelEffectIndexes(db, spellLevelID)
	if err != nil {
		return nil, err
	}
	spellProto.SpellLevelEffectDesc, err = getI18N(
		db, worlddb.STRING_TEXT_TYPE_BUFF_DESC, spellLevelEffectID)
	if err != nil && err != sql.ErrNoRows {
		return nil, err
	}
	spellProto.SpellLevelEffectInfo, err = getSpellLevelEffectInfo(
		db, spellLevelEffectID)
	if err != nil {
		return nil, err
	}
	spellData, err := json.Marshal(
		fusion.MarshalEntityToInterface(&spellProto, "json"))
	if err != nil {
		return nil, err
	}
	return spellData, nil
}

func newSpellInstance(tx *sql.Tx, spellName, spellDesc string) (uint32, error) {
	spellInfo := worlddb.SpellInfo{}
	spellID, err := fusion.SaveJsontableToDB(tx, &spellInfo)
	if err != nil {
		return 0, err
	}
	spellLevelInfo := worlddb.SpellLevelInfo{SpellID: spellID}
	spellLevelID, err := fusion.SaveJsontableToDB(tx, &spellLevelInfo)
	if err != nil {
		return 0, err
	}
	spellLevelEffectInfo := worlddb.
		SpellLevelEffectInfo{SpellID: spellID, SpellLevelID: spellLevelID}
	spellLevelEffectID, err :=
		fusion.SaveJsontableToDB(tx, &spellLevelEffectInfo)
	if err != nil {
		return 0, err
	}
	err = setI18Ns(tx,
		worlddb.STRING_TEXT_TYPE_SPELL_NAME, spellID, spellName,
		worlddb.STRING_TEXT_TYPE_SPELL_DESC, spellID, spellDesc,
		worlddb.STRING_TEXT_TYPE_BUFF_DESC, spellLevelEffectID, "")
	if err != nil {
		return 0, err
	}
	return spellID, nil
}

func newSpellLevelInstance(tx *sql.Tx, spellID uint32) (uint32, error) {
	spellLevelInfo := worlddb.SpellLevelInfo{SpellID: spellID}
	spellLevelID, err := fusion.SaveJsontableToDB(tx, &spellLevelInfo)
	if err != nil {
		return 0, err
	}
	spellLevelEffectInfo := worlddb.
		SpellLevelEffectInfo{SpellID: spellID, SpellLevelID: spellLevelID}
	spellLevelEffectID, err :=
		fusion.SaveJsontableToDB(tx, &spellLevelEffectInfo)
	if err != nil {
		return 0, err
	}
	err = setI18N(tx,
		worlddb.STRING_TEXT_TYPE_BUFF_DESC, spellLevelEffectID, "")
	if err != nil {
		return 0, err
	}
	return spellLevelID, nil
}

func newSpellLevelEffectInstance(
	tx *sql.Tx, spellID, spellLevelID uint32) (uint32, error) {
	spellLevelEffectInfo := worlddb.
		SpellLevelEffectInfo{SpellID: spellID, SpellLevelID: spellLevelID}
	spellLevelEffectID, err :=
		fusion.SaveJsontableToDB(tx, &spellLevelEffectInfo)
	if err != nil {
		return 0, err
	}
	err = setI18N(tx,
		worlddb.STRING_TEXT_TYPE_BUFF_DESC, spellLevelEffectID, "")
	if err != nil {
		return 0, err
	}
	return spellLevelEffectID, nil
}

func copySpellInstance(db *sql.DB, tx *sql.Tx, spellID uint32) (uint32, error) {
	var spellInfo *worlddb.SpellInfo
	spellInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellInfo), "`spellID`=?", spellID)
	if err != nil {
		return 0, err
	}
	if spellInfoVals.Len() < 1 {
		return 0, sql.ErrNoRows
	}
	spellInfo = spellInfoVals.Index(0).Interface().(*worlddb.SpellInfo)
	spellInfo.SpellID = 0
	ID, err := fusion.SaveJsontableToDB(tx, spellInfo)
	if err != nil {
		return 0, err
	}
	err = copyI18Ns(db, tx,
		worlddb.STRING_TEXT_TYPE_SPELL_NAME, spellID, ID,
		worlddb.STRING_TEXT_TYPE_SPELL_DESC, spellID, ID)
	if err != nil {
		return 0, err
	}
	var spellLevelInfo *worlddb.SpellLevelInfo
	spellLevelInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelInfo),
		"`spellID`=? ORDER BY `spellLevelID`", spellID)
	if err != nil {
		return 0, err
	}
	var spellLevelIDsMap = map[uint32]uint32{}
	for i, n := 0, spellLevelInfoVals.Len(); i < n; i++ {
		spellLevelInfo = spellLevelInfoVals.
			Index(i).Interface().(*worlddb.SpellLevelInfo)
		oldSpellLevelID := spellLevelInfo.SpellLevelID
		spellLevelInfo.SpellID, spellLevelInfo.SpellLevelID = ID, 0
		newID, err := fusion.SaveJsontableToDB(tx, spellLevelInfo)
		if err == nil {
			spellLevelIDsMap[oldSpellLevelID] = newID
		} else {
			return 0, err
		}
	}
	var spellLevelEffectInfo *worlddb.SpellLevelEffectInfo
	spellLevelEffectInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelEffectInfo),
		"`spellID`=? ORDER BY `spellLevelEffectID`", spellID)
	if err != nil {
		return 0, err
	}
	for i, n := 0, spellLevelEffectInfoVals.Len(); i < n; i++ {
		spellLevelEffectInfo = spellLevelEffectInfoVals.
			Index(i).Interface().(*worlddb.SpellLevelEffectInfo)
		oldID := spellLevelEffectInfo.SpellLevelEffectID
		spellLevelEffectInfo.SpellLevelEffectID = 0
		spellLevelEffectInfo.SpellID = ID
		spellLevelEffectInfo.SpellLevelID =
			spellLevelIDsMap[spellLevelEffectInfo.SpellLevelID]
		newID, err := fusion.SaveJsontableToDB(tx, spellLevelEffectInfo)
		if err != nil {
			return 0, err
		}
		err = copyI18N(db, tx,
			worlddb.STRING_TEXT_TYPE_BUFF_DESC, oldID, newID)
		if err != nil {
			return 0, err
		}
	}
	return ID, nil
}

func copySpellLevelInstance(
	db *sql.DB, tx *sql.Tx, spellLevelID uint32) (uint32, error) {
	var spellLevelInfo *worlddb.SpellLevelInfo
	spellLevelInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelInfo), "`spellLevelID`=?", spellLevelID)
	if err != nil {
		return 0, err
	}
	if spellLevelInfoVals.Len() < 1 {
		return 0, sql.ErrNoRows
	}
	spellLevelInfo = spellLevelInfoVals.
		Index(0).Interface().(*worlddb.SpellLevelInfo)
	spellLevelInfo.SpellLevelID = 0
	ID, err := fusion.SaveJsontableToDB(tx, spellLevelInfo)
	if err != nil {
		return 0, err
	}
	var spellLevelEffectInfo *worlddb.SpellLevelEffectInfo
	spellLevelEffectInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelEffectInfo),
		"`spellLevelID`=? ORDER BY `spellLevelEffectID`", spellLevelID)
	if err != nil {
		return 0, err
	}
	for i, n := 0, spellLevelEffectInfoVals.Len(); i < n; i++ {
		spellLevelEffectInfo = spellLevelEffectInfoVals.
			Index(i).Interface().(*worlddb.SpellLevelEffectInfo)
		oldID := spellLevelEffectInfo.SpellLevelEffectID
		spellLevelEffectInfo.SpellLevelEffectID = 0
		spellLevelEffectInfo.SpellLevelID = ID
		newID, err := fusion.SaveJsontableToDB(tx, spellLevelEffectInfo)
		if err != nil {
			return 0, err
		}
		err = copyI18N(db, tx,
			worlddb.STRING_TEXT_TYPE_BUFF_DESC, oldID, newID)
		if err != nil {
			return 0, err
		}
	}
	return ID, nil
}

func copySpellLevelEffectInstance(
	db *sql.DB, tx *sql.Tx, spellLevelEffectID uint32) (uint32, error) {
	var spellLevelEffectInfo *worlddb.SpellLevelEffectInfo
	spellLevelEffectInfoVals, err := fusion.LoadJsontableFromDB(
		db, reflect.TypeOf(spellLevelEffectInfo),
		"`spellLevelEffectID`=?", spellLevelEffectID)
	if err != nil {
		return 0, err
	}
	if spellLevelEffectInfoVals.Len() < 1 {
		return 0, sql.ErrNoRows
	}
	spellLevelEffectInfo = spellLevelEffectInfoVals.
		Index(0).Interface().(*worlddb.SpellLevelEffectInfo)
	spellLevelEffectInfo.SpellLevelEffectID = 0
	ID, err := fusion.SaveJsontableToDB(tx, spellLevelEffectInfo)
	if err != nil {
		return 0, err
	}
	err = copyI18N(db, tx,
		worlddb.STRING_TEXT_TYPE_BUFF_DESC, spellLevelEffectID, ID)
	if err != nil {
		return 0, err
	}
	return ID, nil
}

func deleteSpellInstance(tx *sql.Tx, spellID uint32) error {
	tblNames := []string{
		"spell_info",
		"spell_level_info",
		"spell_level_effect_info",
	}
	return fusion.RunDBBatchDelete(tx, tblNames, "spellID", spellID)
}

func deleteSpellLevelInstance(tx *sql.Tx, spellLevelID uint32) error {
	tblNames := []string{
		"spell_level_info",
		"spell_level_effect_info",
	}
	return fusion.RunDBBatchDelete(tx, tblNames, "spellLevelID", spellLevelID)
}

func deleteSpellLevelEffectInstance(tx *sql.Tx, spellLevelEffectID uint32) error {
	tblNames := []string{
		"spell_level_effect_info",
	}
	return fusion.RunDBBatchDelete(tx, tblNames, "spellLevelEffectID", spellLevelEffectID)
}

func saveSpellInstance(tx *sql.Tx, spellProto *SpellPrototype) error {
	err := setI18Ns(tx,
		worlddb.STRING_TEXT_TYPE_SPELL_NAME,
		spellProto.SpellInfo.SpellID, spellProto.SpellName,
		worlddb.STRING_TEXT_TYPE_SPELL_DESC,
		spellProto.SpellInfo.SpellID, spellProto.SpellDesc)
	if err != nil {
		return err
	}
	_, err = fusion.SaveJsontableToDB(tx, spellProto.SpellInfo)
	if err != nil {
		return err
	}
	if spellProto.SpellLevelInfo != nil &&
		spellProto.SpellLevelInfo.SpellLevelID != 0 {
		_, err = fusion.SaveJsontableToDB(tx, spellProto.SpellLevelInfo)
		if err != nil {
			return err
		}
	}
	if spellProto.SpellLevelEffectInfo != nil &&
		spellProto.SpellLevelEffectInfo.SpellLevelEffectID != 0 {
		err = setI18N(tx, worlddb.STRING_TEXT_TYPE_BUFF_DESC,
			spellProto.SpellLevelEffectInfo.SpellLevelEffectID,
			spellProto.SpellLevelEffectDesc)
		if err != nil {
			return err
		}
		_, err = fusion.SaveJsontableToDB(tx, spellProto.SpellLevelEffectInfo)
		if err != nil {
			return err
		}
	}
	return nil
}
